package global

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strings"
	"time"
)

type LogLevel uint8

const (
	Debug LogLevel = iota
	Info           // 默认日志等级
	Error
	Panic
	Fatal
)

var (
	logger *Logger = nil
)

type Logger struct {
	basePath   string
	l          *log.Logger
	level      LogLevel              // 日志等级 debug < info < error < panic < fatal
	prefixFunc func(LogLevel) string // 前缀函数
}

func init() {
	result := new(Logger)
	result.basePath = BasePath
	result.l = log.New(os.Stderr, "", 0)
	result.level = Info
	result.prefixFunc = func(l LogLevel) string {
		switch l {
		case Debug:
			return "~~~~> "
		case Info:
			return "----> "
		case Error:
			return ">>>>> "
		default:
			return "====> "
		}
	}
	logger = result
}

func DefaultLogger() *Logger {
	return logger
}

func CodePosition(skip int, lineOffset int) string {
	_, f, line, ok := runtime.Caller(1 + skip)
	if !ok {
		log.Printf("====> caller info error")
		return ""
	}
	codePath := strings.Replace(f, BasePath, "", 1)
	return fmt.Sprintf(" (code: %s, line: %d) ", codePath, line+lineOffset)
}

func New(l *log.Logger, level LogLevel, pf func(LogLevel) string) *Logger {
	if l == nil || pf == nil {
		return nil
	}
	result := new(Logger)
	result.basePath = BasePath
	result.l = l
	result.level = level
	result.prefixFunc = pf
	return result
}

func (l Logger) GetLevel() LogLevel {
	return l.level
}

func (l *Logger) SetLevel(level LogLevel) *Logger {
	l.level = level
	return l
}

func (l *Logger) Debugf(skip int, format string, args ...any) {
	if l.level > Debug {
		return
	}
	_, f, line, ok := runtime.Caller(1 + skip)
	if !ok {
		l.l.Printf("====> caller info error")
		l.l.Printf("====> "+format, args...)
		return
	}
	codePath := strings.Replace(f, l.basePath, "", 1)
	codeInfo := fmt.Sprintf(" (code: %s, line: %d, datetime: %s) ", codePath, line, time.Now().Format(time.DateTime))
	l.l.Printf(l.prefixFunc(Debug)+format+codeInfo, args...)
}

// 打印信息级日志
func (l *Logger) Infof(skip int, format string, args ...any) {
	if l.level > Info {
		return
	}
	_, f, line, ok := runtime.Caller(1 + skip)
	if !ok {
		l.l.Printf("====> caller info error")
		l.l.Printf("====> "+format, args...)
		return
	}
	codePath := strings.Replace(f, l.basePath, "", 1)
	codeInfo := fmt.Sprintf(" (code: %s, line: %d, datetime: %s) ", codePath, line, time.Now().Format(time.DateTime))
	l.l.Printf(l.prefixFunc(Info)+format+codeInfo, args...)
}

// 打印警告级日志
func (l *Logger) Errorf(skip int, format string, args ...any) {
	if l.level > Error {
		return
	}
	_, f, line, ok := runtime.Caller(1 + skip)
	if !ok {
		l.l.Printf("====> caller info error")
		l.l.Printf("====> "+format, args...)
		return
	}
	codePath := strings.Replace(f, l.basePath, "", 1)
	codeInfo := fmt.Sprintf(" (code: %s, line: %d, datetime: %s) ", codePath, line, time.Now().Format(time.DateTime))
	l.l.Printf(l.prefixFunc(Error)+format+codeInfo, args...)
}

// 打印日志并Panic
func (l *Logger) Panicf(skip int, format string, args ...any) {
	if l.level > Panic {
		return
	}
	_, f, line, ok := runtime.Caller(1 + skip)
	if !ok {
		l.l.Printf("====> caller info error")
		l.l.Printf("====> "+format, args...)
		return
	}
	codePath := strings.Replace(f, l.basePath, "", 1)
	codeInfo := fmt.Sprintf(" (code: %s, line: %d, datetime: %s) ", codePath, line, time.Now().Format(time.DateTime))
	l.l.Panicf(l.prefixFunc(Panic)+format+codeInfo, args...)
}

func (l *Logger) Fatalf(skip int, format string, args ...any) {
	if l.level > Fatal {
		return
	}
	_, f, line, ok := runtime.Caller(1 + skip)
	if !ok {
		l.l.Printf("====> caller info error")
		l.l.Printf("====> "+format, args...)
		return
	}
	codePath := strings.Replace(f, l.basePath, "", 1)
	codeInfo := fmt.Sprintf(" (code: %s, line: %d, datetime: %s) ", codePath, line, time.Now().Format(time.DateTime))
	l.l.Fatalf(l.prefixFunc(Fatal)+format+codeInfo, args...)
}
